package com.guosheng.nio;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {

    //标识数字
    private int flag = 0;

    //缓冲区大小
    private int BLOCK = 4096;

    //接受收据缓冲区
    private ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);

    //发送数据缓冲区
    private ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);

    //选择器
    private Selector selector;

    private NIOServer(int port) throws Exception{
        //打开服务器套接字通道
        ServerSocketChannel server = ServerSocketChannel.open();
        //服务器配置为非阻塞
        server.configureBlocking(false);
        //检索与此通道关联的服务器套接字
        ServerSocket serverSocket = server.socket();
        //进行服务的绑定
        serverSocket.bind(new InetSocketAddress(port));
        //通过open方法找到Selector
        selector = Selector.open();
        //注册到selector，等待连接
        server.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("server start in port :" + port);
    }

    private void listen() throws Exception{
        while(true){
            //选择一组键，并且相应的通道已经打开
            selector.select();
            //返回此选择器的已选择键集
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            //获取键集的迭代器
            Iterator<SelectionKey> it = selectionKeys.iterator();
            while(it.hasNext()){
                SelectionKey key = it.next();
                it.remove();
                handleKey(key);
            }

        }
    }

    //处理请求
    private void handleKey(SelectionKey key) throws Exception{
        //接受请求
        ServerSocketChannel server = null;
        SocketChannel client = null;
        String receiveText = null;
        String sendText = null;
        int count = 0;
        //测试此键的通道是否已准备好接受新的套接字连接
        if(key.isAcceptable()){
            //返回为之创建此键的通道
            server = (ServerSocketChannel) key.channel();
            //接受到此通道套接字的连接
            //此方法返回套接字通道（如果有）将处于阻塞模式
            client = server.accept();
            //配置为非阻塞
            client.configureBlocking(false);
            //注册到Selector等待连接，注册可读事件
            client.register(selector,SelectionKey.OP_READ);
        }else if(key.isReadable()){
            //返回为之创建此键的通道
            client = (SocketChannel)key.channel();
            //将缓冲区清空以备下次读取
            receiveBuffer.clear();
            //读取客户端发送来的数据到缓冲区中
            count = client.read(receiveBuffer);
            if(count > 0){
                receiveText = new String(receiveBuffer.array(),0,count,"utf-8");
                System.out.println("服务器端接受客户端数据----为：" +receiveText);
                //注册可写事件
                client.register(selector,SelectionKey.OP_WRITE);
            }
        }else if(key.isWritable()){
            //将缓冲区清空以备下次写入
            sendBuffer.clear();
            //返回为之创建此键的通道
            client = (SocketChannel) key.channel();
            sendText = "郭胜，是最棒的，lalalala……"+ flag ++;
            //向缓冲区中输入数据
            sendBuffer.put(sendText.getBytes());
            //将缓冲区各标志复位，因为向里面put了数据标志被改变要想从中读取数据发向客户端，就要复位
            sendBuffer.flip();
            //输出到通道
            client.write(sendBuffer);
            System.out.println("服务器端向客户端发送数据-----为：" + sendText);
            client.register(selector,SelectionKey.OP_READ);
        }
    }

    public static void main(String[] args){
        try {
            NIOServer server = new NIOServer(7070);
            server.listen();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
